﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

namespace VerficationsWPF.HWControls
{
    /// <summary>
    /// ButifulDialog.xaml 的交互逻辑，通过编写 Func<object> MainTask 指定需要执行的任务。
    /// </summary>
    public partial class TaskWindow : Window
    {
        /// <summary>
        /// 主任务，在调用窗体前需指定。
        /// </summary>
        public Func<object> MainTask = default!;

        /// <summary>
        /// 任务失败时调用的操作，未实现，保留。
        /// </summary>
        public Action? FailTask;

        /// <summary>
        /// 执行的结果。
        /// </summary>
        public object? Result;

        /// <summary>
        /// 默认最大等待时间，如果超时则关闭窗体。
        /// </summary>
        public int Timeout = 60;

        /// <summary>
        /// 退出时返回的消息，默认为空表示正常执行。
        /// </summary>
        public string ExitMessage = "";

        /// <summary>
        /// 是否在执行完任务后自动关闭窗体，默认值为true。否则需要手工点击。
        /// </summary>
        public bool AutoCloseWindow = true;


        CancellationTokenSource cts = new CancellationTokenSource();

        /// <summary>
        /// 界面上显示的主要信息文本。
        /// </summary>
        public string Info
        {
            get => tbContent.Text;
            set
            {
                tbContent.Text = value;
            }
        }
         
        public TaskWindow()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            Close();
        }

        /// <summary>
        /// 以指定控件为中心进行显示。
        /// </summary>
        /// <param name="element"></param>
        public bool? ShowAt(FrameworkElement element)
        {
            if (double.IsNaN(element.ActualWidth) || double.IsNaN(element.ActualHeight))
            {
                WindowStartupLocation = WindowStartupLocation.CenterScreen;
                return ShowDialog();
            }
            else
            {
                WindowStartupLocation = WindowStartupLocation.Manual;
                var leftTop = element.PointToScreen(new Point(0, 0));
                
                this.Left = leftTop.X + (element.ActualWidth - this.Width) / 2;
                this.Top = leftTop.Y + (element.ActualHeight - this.Height) / 2;
                return ShowDialog();
            }
        }

        bool isLeftMouseDown = false;
        Point lastMousePosition = new Point();
        Point lastWindowPosition = new Point();

        private void Border_MouseDown(object sender, MouseButtonEventArgs e)
        {
            if(e.LeftButton == MouseButtonState.Pressed)
            {
                var win = Window.GetWindow(this);
                isLeftMouseDown = true;
                lastMousePosition = this.PointToScreen(Mouse.GetPosition(this));
                lastWindowPosition = new Point(win.Left, win.Top);
                Debug.WriteLine($"{PointToScreen(Mouse.GetPosition(this))}  {lastMousePosition}");
            }
        }

        private void Border_MouseMove(object sender, MouseEventArgs e)
        {
            if (isLeftMouseDown)
            {
                Debug.WriteLine($"{Mouse.GetPosition(this)} {this.PointToScreen(Mouse.GetPosition(this))}");
                // 新的鼠标屏幕坐标
                var new_pos = this.PointToScreen(Mouse.GetPosition(this));

                Point offset = new Point(new_pos.X - lastMousePosition.X, new_pos.Y - lastMousePosition.Y);
                var win = Window.GetWindow(this);
                win.Left = lastWindowPosition.X + offset.X;
                win.Top = lastWindowPosition.Y + offset.Y;
            }
        }

        private void Border_MouseUp(object sender, MouseButtonEventArgs e)
        {
            isLeftMouseDown = false;
        }


        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            Result = null;
            TaskFactory taskFactory = new TaskFactory(cts.Token);
            taskFactory.StartNew(() =>
            {
                // 等等窗体显示
                Thread.Sleep(1000);

                // 为避免主任务为空，所以进行了检测。
                if (MainTask == null)
                {
                    Result = null;
                    ExitMessage = "MainTask is null.";
                    return;
                }

                // 如果不加可能会出现因窗体关闭后发生数据交互导致的错误。
                try
                {
                    var res = taskFactory.StartNew(MainTask);
                    if (res.Wait((Timeout - 1) * 1000)) // 开始休眠了1秒
                    {
                        Result = res.Result;
                    }
                    else if (cts.IsCancellationRequested)
                    {
                        ExitMessage = "Cancelled";
                    }
                    else
                    {
                        ExitMessage = "Timeout";
                    }

                }
                catch (Exception ex)
                {
                    Result = null;
                    ExitMessage = "Error: " + ex.Message;
                }

                if (Result == null && FailTask != null)
                    new TaskFactory().StartNew(FailTask).Wait(1000);

                // 如果设置为自动关闭窗体，则会调用Close方法，但由于此时窗体可能已经关闭或其他未知原因，所以用try-catch更为安全
                if (AutoCloseWindow)
                {
                    this.Dispatcher.Invoke(() =>
                    {
                        try
                        {
                            Close();
                        }
                        catch { }
                    });
                }
            });

            // 测试功能，用于显示倒记时的数字
            Task.Run(() =>
            {
                try
                {
                    int tid = 0;
                    string[] dots = new string[] { "", ".", "..", "...", "....", ".....", "......" };
                    var endTime = DateTime.Now.AddSeconds(Timeout);
                    while (DateTime.Now < endTime)
                    {
                        var ts = endTime - DateTime.Now;
                        if (ts.TotalSeconds < 0)
                        {
                            Dispatcher.Invoke(() => Info = $"时间到");
                            break;
                        }
                        else
                        {
                            string str = $"任务执行中 {ts.TotalSeconds:0.0} seconds.";
                            str = $"后台在努力处理任务执行中{dots[tid]}";
                            Dispatcher.Invoke(() => Info = str);
                            tid = (tid + 1) % dots.Length;
                        }
                        Thread.Sleep(200);
                    }
                }
                catch (Exception ex)
                {
                    ExitMessage = "Error: " + ex.Message;
                }
            });
        }

        private void btnClose_Click(object sender, RoutedEventArgs e)
        {
            ExitMessage = "Cancelled";
            cts.Cancel();
            Close();
        }
    }
}
